package com.shujia.day16.udp.send.file;


import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.ArrayList;

public class Server {
    public static void main(String[] args) throws IOException, InterruptedException {
        /*
            TODO
                之前完成的都是只能上传一个文件，文件上传完成后，程序停止
                    如果要连续上传多个文件如何操作呢？
         */
        FilePath filePath = new FilePath();
        System.out.println("程序开始执行了... 创建了文件对象用于接收文件路径...");
        GetNameThread thread1 = new GetNameThread("线程2", filePath, 9999);
        thread1.start();
        GetDataThread thread = new GetDataThread("线程1", filePath, 9998);
        thread.start();
    }

    static class FilePath {
        String pathName;
        boolean isOut; // 当前文件路径是否被创建过输出流

        boolean outStatus;

        public FilePath() {
            this.pathName = null;
            this.isOut = true;
            this.outStatus = false;
        }

        public String getPathName() {
            return pathName;
        }

        public void setPathName(String pathName) {
            this.pathName = pathName;
        }

        public boolean isOut() {
            return isOut;
        }

        public void setOut(boolean out) {
            isOut = out;
        }

        public boolean isOutStatus() {
            return outStatus;
        }

        public void setOutStatus(boolean outStatus) {
            this.outStatus = outStatus;
        }


    }

    static class GetDataThread extends Thread {
        FilePath filePath;
        int capacity;
        DatagramSocket socket;

        public GetDataThread(String name, FilePath filePath, int port) {
            super(name);
            this.filePath = filePath;
            try {
                socket = new DatagramSocket(port);
            } catch (SocketException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Socket Data 启动了...");
            capacity = 1024;
        }

        @Override
        public void run() {
            /*
                TODO 获取数据
                    ① 获取数据之前需要先获取文件的输出路径，如果路径是null 或者 isOut是true
                            路径不存在，或者已经存在的路径 之前已经被创建过输出对象，写出到磁盘中了
                    ② 当路径获取到之后，再重写出数据
             */
            System.out.println("文件输出线程已经启动...");
            try {
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    synchronized (filePath) {
                        if (filePath.getPathName() == null || filePath.isOut()) {
                /*
                    TODO 处于等待其他线程获取文件名
                        1.对  filePath 进行加锁，并且 让当前线程处于 等待阻塞状态

                 */
                            try {
                                System.out.println("当前文件名为空,或者文件已经被输出后，当前文件线程需要被阻塞等待名称线程唤醒...");
                                filePath.wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }

                        } else {
                            // TODO 开始创建IO流写出数据 => 直到其写出完成后，再进行修改参数
                            //     1.将数据写出 => 创建IO流
                            //       每个文件都需要有自身的输出流

                            data2IO();
                            // TODO 当文件上传完成后，需要通知文件名称的线程继续接收文件名
                            filePath.notify();
                            // TODO 当一个文件接收完成后，那么当前线程需要处于阻塞状态 等待名称线程的唤醒
                            try {
                                filePath.wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                socket.close();
            }
        }

        public void data2IO() {
            try {
                // TODO 标记状态，当前文件数据正在接收状态
                filePath.outStatus = true;
                int readLen = 0;
                BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath.getPathName()));
                System.out.println("数据已经创建好IO流开始写出数据到磁盘...");
                byte[] imageBytes = new byte[capacity];
                DatagramPacket imagePacket = new DatagramPacket(imageBytes, imageBytes.length);
                socket.receive(imagePacket);
                while ((readLen = imagePacket.getLength()) > 0) {
                    // TODO 将读取到的数据以IO流的方式保存下来
                    System.out.println("获取到一部分文件数据数据..");
                    bufferedOutputStream.write(imagePacket.getData(), 0, readLen);
                    bufferedOutputStream.flush();
                    // TODO 当文件数据接收完成后，需要将状态修改成非输出状态，并且将IO流进行关闭，等待下一次接收数据
                    if (readLen != capacity) {
                        System.out.println("数据文件已经上传完成...");
                        filePath.outStatus = false;
                        filePath.isOut = true;
                        System.out.println("输出完成，关闭输出流");
                        bufferedOutputStream.close();
                        break;
                    }
                    socket.receive(imagePacket);
                }
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

    }

    static class GetNameThread extends Thread {
        FilePath filePath;
        DatagramSocket dataNameSocket;
        byte[] bytes;

        int capacity = 1024;

        public GetNameThread(String name, FilePath filePath, int port) {
            super(name);
            this.filePath = filePath;
            try {
                dataNameSocket = new DatagramSocket(port);
                System.out.println("Name Socket 启动了...");
            } catch (Exception e) {
                e.printStackTrace();
            }
            bytes = new byte[capacity];
        }

        public void setCapacity(int capacity) {
            this.capacity = capacity;
        }

        @Override
        public void run() {
            /*
                TODO 获取文件名称
                    1.当文件在上传时，文件名不需要进行获取  当前线程处于阻塞状态
                         文件是否上传可以通过 变量判断
             */
            System.out.println("获取名称的线程启动了....");
            try {
                while (true) {
                    Thread.sleep(1000);
                    synchronized (filePath) {
                        // 文件没有被写入磁盘或者文件正在被写
                        if (!filePath.isOut || filePath.outStatus) {
                            // 表示当前数据接收线程正在处于 运行状态
                            System.out.println("文件正在输出...当前获取文件的线程睡眠...");
                            try {
                                filePath.wait();
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                        } else {
                            System.out.println("正在等待获取文件名称...");
                            // 否则表示当前数据接收的线程正在处于闲置状态
                            getOneFileName();
                        }
                    }

                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                dataNameSocket.close();
            }
        }

        public void getOneFileName() {
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
            int readLen = 0;

            try {
                // TODO 通过 6666 端口接收数据
                dataNameSocket.receive(datagramPacket);  // 阻塞 => 客户端有数据发送过来，那么才继续执行
                System.out.println("接收到文件名称数据包...");
                String path = "";
                while ((readLen = datagramPacket.getLength()) > 0) {
                    // TODO 假设客户端对数据分多个包发送 每次拿到一个数据包 进行字符串拼接
                    path += new String(datagramPacket.getData(), 0, readLen);
                    if (readLen != capacity) {
                        break;
                    }
                    dataNameSocket.receive(datagramPacket);
                }
                // TODO 为了防止客户端发送空包，可以对路径长度进行判断
                if (path.length() > 0) {
                    System.out.println("接收到文件路径了..."+path);
                    filePath.setPathName(path);
                    filePath.isOut = false;  // 对新添加的文件路径要设置输出标记为false
                    // TODO 当有新的文件添加后，要求获取数据的线程开始运行 => 唤醒
                    System.out.println("开始启动数据获取线程,获取数据....");
                    filePath.notify();
                    filePath.wait();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
